import type { RouteLocationNormalized, RouteMeta, RouteRecordNormalized } from 'vue-router'
import { isExternal } from '@/utils/is'
import qs from 'qs'
import { defineAsyncComponent } from 'vue'
import { pathResolve } from '@/utils/index'
// TransparentView足够简单，无需动态加载
// import TransparentView from '@/layout/TransparentView.vue'

/**
 * {
 *     '../views/Home/Index.vue': () => import('/src/views/Home/Index.vue'),
 *     '../views/Login/Login.vue': () => import('/src/views/Login/Login.vue'),
 *     '../views/system/dict/index.vue': () => import('/src/views/system/dict/index.vue'),
 * }
 */
const modules = import.meta.glob('../views/**/*.{vue,tsx}')

export const Layout = () => import('@/layout/Layout.vue')

const component_iframe = 'base/other/IframeView'

/**
 * 注册一个异步组件
 * @param componentPath 如：/system/user/index
 */
export const registerComponent = (componentPath: string) => {
  for (const item in modules) {
    if (item.includes(componentPath)) {
      // @ts-ignore
      return defineAsyncComponent(modules[item])
    }
  }
}

export const getRawRoute = (route: RouteLocationNormalized): RouteLocationNormalized => {
  if (!route) return route
  const { matched, ...opt } = route
  return {
    ...opt,
    matched: (matched
      ? matched.map((item) => ({
          meta: item.meta,
          name: item.name,
          path: item.path
        }))
      : undefined) as RouteRecordNormalized[]
  }
}

/**
 * 将服务端用户授权菜单数据转换为前端路由数据结构
 * @param routes
 * @param level 当前路由层级
 */
export const generateRoute = (
  routes: AppCustomRouteRecordRaw[],
  level = 1
): AppRouteRecordRaw[] => {
  const res: AppRouteRecordRaw[] = []
  const moduleRouteKeys = Object.keys(modules)
  for (const routeData of routes) {
    // 生成route.meta
    const meta: RouteMeta = {
      title: routeData.name,
      icon: routeData.icon,
      hidden: !routeData.visible,
      noCache: !routeData.keepAlive,
      alwaysShow:
        routeData.children && routeData.children.length === 1 && (routeData.alwaysShow ?? true)
    }
    // 如果服务端配置路由组件包含?xx=xx，则表示需要传递参数，此时解析到meta.query中
    // 后续vue中通过const route = useRoute(); route.meta.query获取
    // 关于qs:
    // const str = 'name=John&age=30&hobbies=reading&hobbies=traveling';
    // const parsedObj = qs.parse(str);
    // parsedObj: { name: 'John', age: '30', hobbies: ['reading', 'traveling'] }
    if (routeData.component && routeData.component.indexOf('?') > -1) {
      const fragments = routeData.component.split('?')
      routeData.component = fragments[0]
      meta.query = qs.parse(fragments[1])
    }

    // 生成route
    let route: AppRouteRecordRaw = {
      // 非外链移除path中的?xxx部分(可能服务端误填，没有任何用途)
      path:
        routeData.path.indexOf('?') > -1 && !isExternal(routeData.path)
          ? routeData.path.split('?')[0]
          : routeData.path,
      // 若为设置组件名称，则以path驼峰式转首字母大写后作为组件名称，以便支持keep alive，需要与组件实际名称一致才可生效
      name:
        routeData.componentName && routeData.componentName.length > 0
          ? routeData.componentName
          : toCamelCase(routeData.path, true),
      redirect: routeData.redirect,
      meta: meta
    }

    // 顶层非目录组件内链路由
    if (routeData.parentId === 0 && routeData.type === 'M' && routeData.component) {
      // 顶层内链路由，需要在vue route中构建父子层次关系，因为需要父route的Layout组件进行主体布局渲染(主体布局router-view填充)，可参考remaining.ts中的首页Home
      // 使用原有route构造子菜单route
      const childRoute: AppRouteRecordRaw = { ...route, path: '' }
      route.component = Layout
      route.meta = {} // alwaysShow默认为false，这个构造的目录(拥有单独子菜单)展示时会自动提升
      route.name = toCamelCase(routeData.path, true) + 'Parent'
      route.redirect = ''
      meta.alwaysShow = true // 不一定需要
      const index = moduleRouteKeys.findIndex((modulePath) =>
        modulePath.includes(routeData.component)
      )
      childRoute.component = modules[moduleRouteKeys[index]]
      if (
        routeData.component === component_iframe &&
        isExternal(routeData.path) &&
        routeData.componentName
      ) {
        route.path = pathResolve('/', routeData.componentName)
        childRoute.meta.link = routeData.path
      }
      route.children = [childRoute]
    } else {
      if (routeData.type === 'D') {
        // 目录
        // route.component = level === 1 ? Layout : TransparentView
        /*
          vue3允许中间层router没有component，仅用于描述url层次，不必拥有组件展示，这样就不会影响AppView中的keep-alive逻辑，keep-alive缓存的是最终route的组件名，
          如果中间层也有component，那么keep-alive的直接子组件就是这里的中间层组件(TransparentView)，其名称并不是keep-alive组件缓存的最终route组件名，导致keepalive
          效果不生效；后续如果不支持这种undefined component值，那就需要撤销中间层route，直接摊平到只有两层route，比如/system/messages/mail/mail-account摊平为:
          ['/system', 'messages/mail/mail-account']，/system/messages/mail/mail-template摊平为: ['/system', 'messages/mail/mail-template']
         */
        route.component = level === 1 ? Layout : undefined
        route.redirect = ''
        // 暂时移除目录路径跳转下级第一个子菜单路径的逻辑
        // data.redirect = getRedirect(routeData.path, routeData.children)
      } else if (isExternal(routeData.path) && routeData.component !== component_iframe) {
        // 外链
        route = {
          path: '/external-link',
          component: Layout,
          meta: {},
          children: [route]
        } as AppRouteRecordRaw
      } else {
        // 菜单
        // 后端定义菜单使用组件地址，或者组件地址不设置，直接按照前端的路径编写路由地址path(不推荐)
        const index = routeData?.component
          ? moduleRouteKeys.findIndex((modulePath) => modulePath.includes(routeData.component))
          : moduleRouteKeys.findIndex((modulePath) => modulePath.includes(routeData.path))
        route.component = modules[moduleRouteKeys[index]]
        if (
          routeData.component === component_iframe &&
          isExternal(routeData.path) &&
          routeData.componentName
        ) {
          route.path = routeData.componentName
          route.meta.link = routeData.path
        }
      }
      if (routeData.type === 'D' && routeData?.children?.length) {
        route.children = generateRoute(routeData.children, level + 1)
      }
    }
    res.push(route)
  }
  return res
}

// 构造目录的跳转路径，父路径s + 第一个子菜单路径
export const getRedirect = (parentPath: string, children: AppCustomRouteRecordRaw[]) => {
  if (!children || children.length == 0) {
    return parentPath
  }
  const path = pathResolve(parentPath, children[0].path)
  if (!children[0].children || children[0].children.length == 0) {
    return path
  }
  return getRedirect(path, children[0].children)
}

const toCamelCase = (str: string, upperCaseFirst: boolean) => {
  str = (str || '')
    .replace(/-(.)/g, function (group1: string) {
      return group1.toUpperCase()
    })
    .replaceAll('-', '')

  if (upperCaseFirst && str) {
    str = str.charAt(0).toUpperCase() + str.slice(1)
  }

  return str
}
